package steampunk;

import steampunk.art.Art;
import steampunk.entities.Entity;
import steampunk.entities.PlayerEntity;
import steampunk.utils.CategoryTable;
import steampunk.utils.CountTable;

import java.awt.Color;
import java.util.*;

/**
 * Singleton class containing all the game's state information
 * @author garrett
 *
 */
public class GameVars {
	
	/** The different collections of entities */
	private List<Entity> allEntities, dynamicEntities, staticEntities,
		backgroundEntities, foregroundEntities, solidEntities;
	
	/** Purely graphical effects, like explosions and stuff */
	private List<Art> effects;
	
	private PlayerControls pcontrols;
	
	private PlayerEntity player;
	
	/** The width of the game */
	public int aspectWidth = 640;
	/** The height of the game */
	public int aspectHeight = 480;
	
	/** The gravity of the game (pixels/second) */
	public double GRAVITY = 9.8*250;
	
	public final Object elock = new Object();
	
	private CountTable<Entity> entityCount;
	
	private CategoryTable<Integer, Entity> depthTable;
	
	public Color wireColor = Color.WHITE;
	
	/**
	 * Returns the horizontal center of the player
	 * @return
	 */
	public double playerX() {
		if (player == null) return 0;
		return player.centerX();
	}
	
	/**
	 * Returns the vertical center of the player
	 * @return
	 */
	public double playerY() {
		return player.centerY();
	}
	
	public PlayerEntity getPlayer() {
		if (player == null) {
			player = new PlayerEntity(300, 50);
			addEntity(player);
		}
		return player;
	}
	
	public int getNumberZLevels() {
		return depthTable.getCategoryCount();
	}
	
	public List<Entity> zSortedEntities() {
		PriorityQueue<Integer> levels = new PriorityQueue<Integer>();
		for (Integer i : depthTable.getCategories()) {
			levels.add(-i); // make sure it sorts from greatest to least
		}
		LinkedList<Entity> result = new LinkedList<Entity>();
		for (int i : levels) {
			for (Entity e : depthTable.getCategoryList(-i))
				result.add(e);
		}
		return result;
	}
	
	public List<Entity> getAllEntities() {
		return allEntities;
	}
	
	public List<Entity> getDynamicEntities() {
		return dynamicEntities;
	}
	
	public List<Entity> getStaticEntities() {
		return staticEntities;
	}
	
	public List<Entity> getSolidEntities() {
		return solidEntities;
	}
	
	public List<Art> getEffects() {
		return effects;
	}
	
	public PlayerControls getPlayerControls() {
		if (pcontrols == null)
			pcontrols = new PlayerControls();
		return pcontrols;
	}
	
	/**
	 * Returns true if the canvas should be repainted this frame
	 * @return
	 */
	public boolean shouldRefreshDisplay() {
		return true;
	}
	
	public void addEffect(Art art) {
		synchronized (elock) {
			if (!effects.contains(art))
				effects.add(art);
		}
	}
	
	public void removeEntity(Entity entity) {
		synchronized (elock) {
			if (entityCount.count(entity) > 0) {
				entityCount.remove(entity);
				if (entity.isStatic()) {
					staticEntities.remove(entity);
				} else {
					dynamicEntities.remove(entity);
				}
				
				depthTable.remove(entity.getZLevel(), entity);
				
				if (entity.isSolid()) {
					solidEntities.remove(entity);
				}
				
				allEntities.remove(entity);
			}
		}
	}
	
	public void addEntity(Entity entity) {
		synchronized (elock) {
			if (entityCount.count(entity) == 0) {
				entityCount.add(entity);
				if (entity.isStatic()) {
					staticEntities.add(entity);
				} else {
					dynamicEntities.add(entity);
				}
				
				depthTable.add(entity.getZLevel(), entity);
				
				if (entity.isSolid()) {
					solidEntities.add(entity);
				}
				
				allEntities.add(entity);
			}
		}
	}

	
	// Code to create singleton
	private static GameVars self = null;
	public static GameVars getInstance() {
		if (self == null) self = new GameVars();
		return self;
	}
	
	private GameVars() {
		entityCount = new CountTable<Entity>();
		
		effects = Collections.synchronizedList(new LinkedList<Art>());
		
		staticEntities= Collections.synchronizedList(new LinkedList<Entity>());
		dynamicEntities = Collections.synchronizedList(new LinkedList<Entity>());
		allEntities = Collections.synchronizedList(new LinkedList<Entity>());
		solidEntities = Collections.synchronizedList(new LinkedList<Entity>());
		backgroundEntities = Collections.synchronizedList(new LinkedList<Entity>());
		foregroundEntities = Collections.synchronizedList(new LinkedList<Entity>());
		depthTable = new CategoryTable<Integer, Entity>();
	}
}
